package top.tenebrous.yzl.im.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import top.tenebrous.yzl.im.message.Message;
import top.tenebrous.yzl.im.message.MessageBody;
import top.tenebrous.yzl.im.message.MessageResponse;
import top.tenebrous.yzl.im.model.ImClientUserBean;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author yazhang6
 */
@Slf4j
@Component
public class WebSocketUtils {

    // userId为维度的session集合
    private static final Map<String, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();

    // session为维度的session集合
    private static final Map<WebSocketSession, String> SESSION_USER_MAP = new ConcurrentHashMap<>();

    // 群组（在线session）
    private static final Map<String, List<WebSocketSession>> SESSION_USER_GROUP_MAP = new ConcurrentHashMap<>();

    // 服务端session集合  以clientId为维度
    private static final Map<String, WebSocketSession> SERVER_SESSION_MAP = new ConcurrentHashMap<>();

    // 服务端session集合  以session为维度
    private static final Map<WebSocketSession, String> SESSION_SERVER_MAP = new ConcurrentHashMap<>();

    // 服务端已链接的地址集合
    private static final List<String> CONNECTED_LIST = new ArrayList<>();

    public static void addConnectedSession(String url) {
        CONNECTED_LIST.add(url);
    }

    public static List<String> getConnectedSession() {
        return CONNECTED_LIST;
    }

    public static void removeConnectedSession(String url) {
        CONNECTED_LIST.remove(url);
    }

    public static void addSession(WebSocketSession session, ImClientUserBean memUserDO, List<String> userGroupsIds) {
        USER_SESSION_MAP.put(memUserDO.getUserId(), session);
        SESSION_USER_MAP.put(session, memUserDO.getUserId());
        // TODO 需要获取当前用户所属群组列表
        for (String groupId : userGroupsIds) {
            // 判断群组session是否存在
            List<WebSocketSession> sessions = SESSION_USER_GROUP_MAP.get(groupId);
            if (CollectionUtils.isEmpty(sessions)) {
                sessions = new ArrayList<>();
            }
            sessions.add(session);
            SESSION_USER_GROUP_MAP.put(groupId, sessions);
        }
        log.info("当前在线session:{}", JSON.toJSONString(USER_SESSION_MAP.keySet()));
    }

    /**
     * 添加服务端session
     *
     * @param clientId      服务id
     * @param session       session
     */
    public static void addServerSession(String clientId ,WebSocketSession session) {
        SERVER_SESSION_MAP.put(clientId, session);
        SESSION_SERVER_MAP.put(session, clientId);
    }

    /**
     * 移除该服务端session
     *
     * @param session
     */
    public static void removeServerSession(WebSocketSession session) {
        String clientId = SESSION_SERVER_MAP.remove(session);
        SERVER_SESSION_MAP.remove(clientId);
    }

    public static WebSocketSession getServerSessionByClientId(String clientId) {
        return SERVER_SESSION_MAP.get(clientId);
    }

    public static String getServerSessionBySession(WebSocketSession session) {
        return SESSION_SERVER_MAP.get(session);
    }

    /**
     * 根据userId获取session
     *
     * @param userId    用户id
     * @return          session
     */
    public static WebSocketSession getSession(String userId) {
        return USER_SESSION_MAP.get(userId);
    }

    /**
     * 根据session获取用户id
     *
     * @param session     session
     * @return            用户id
     */
    public static String getUserIdBySession(WebSocketSession session) {
        return SESSION_USER_MAP.get(session);
    }

    /**
     * 根据组id获取session集合
     *
     * @param groupId       组id
     * @return              该组下的所有用户session
     */
    public static List<WebSocketSession> getSessionGroup(String groupId) {
        return SESSION_USER_GROUP_MAP.get(groupId);
    }

    /**
     * 移除该session
     *
     * @param session
     */
    public static void removeSession(WebSocketSession session) {
        if (Objects.isNull(session)) {
            log.info("当前服务无该session信息");
            return;
        }
        // 移除session
        String userId = SESSION_USER_MAP.remove(session);
        if (StringUtils.isNotBlank(userId)) {
            USER_SESSION_MAP.remove(userId);

            for (Map.Entry<String, List<WebSocketSession>> entry : SESSION_USER_GROUP_MAP.entrySet()) {
                List<WebSocketSession> sessions = entry.getValue();
                sessions.remove(session);
            }
        }
        log.info("当前在线session:{}", JSON.toJSONString(USER_SESSION_MAP.keySet()));
    }

    public static <T> void send(WebSocketSession session, String type, T message) throws JSONException, IOException {
        String messageText = buildTextMessage(type, message);
        sendTextMessage(session, messageText);
        log.info("发送个人消息开始 发送内容{}", JSON.toJSONString(message));
    }

    public static <T> void sendGroup(List<WebSocketSession> sessions, String type, T message) throws JSONException, IOException {
        String messageText = buildTextMessage(type, message);
        for (WebSocketSession session : sessions) {
            sendTextMessage(session, messageText);
        }
        log.info("发送群组消息完成 发送内容{}", JSON.toJSONString(message));
    }

    @SneakyThrows
    public static <T> void sendError(WebSocketSession session, String type, String errorMsg, String fromId) {
        String messageText = buildTextErrorMessage(type, errorMsg, fromId);
        sendTextMessage(session, messageText);
        log.info("发送错误消息内容完成 发送内容{} 接收者id:{}",  errorMsg, fromId);
    }

    private static String buildTextErrorMessage(String type, String errorMsg, String fromId) {
        JSONObject messageObject = new JSONObject();
        MessageResponse<JSONObject> response = new MessageResponse<>();
        response.setCode(500);
        response.setMsg(errorMsg);
        messageObject.put("type", type);
        messageObject.put("body", new MessageBody(fromId));
        response.setData(messageObject);
        return JSON.toJSONString(response);
    }

    public static <T> String buildTextMessage(String type, T message) throws JSONException {
        JSONObject messageObject = new JSONObject();
        MessageResponse<JSONObject> response = new MessageResponse<>();
        messageObject.put("type", type);
        messageObject.put("body", message);
        response.setData(messageObject);
        return JSON.toJSONString(response);
    }

    private static void sendTextMessage(WebSocketSession session, String messageText) throws IOException {
        if (session == null) {
            return;
        }
        session.sendMessage(new TextMessage(messageText));
    }

    public static <T extends Message> void broadcast(String type, T message) throws IOException, JSONException {
        String messageText = buildTextMessage(type, message);
        for (WebSocketSession session : SESSION_USER_MAP.keySet()) {
            sendTextMessage(session, messageText);
        }
    }


}
